<?php

namespace Ionafan2\GetonlyimgBundle\Services;

use Doctrine\ORM\EntityManager;
use Ionafan2\GetonlyimgBundle\Entity;
use Symfony\Bridge\Monolog\Logger;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;

/**
 * Class Parser
 * @TODO как и стоит ли вообще парсить vk.com, plus.google.com/photos/, www.facebook.com, twitter.com/*\/media/grid
 * @package Ionafan2\GetonlyimgBundle\Services
 */
class Parser {

    /**
     * @var \Doctrine\ORM\EntityManager
     */
    protected $em;

    /**
     * @var \Symfony\Bridge\Monolog\Logger
     */
    protected $logger;

    /**
     * @var \Symfony\Component\HttpFoundation\Session\Session
     */
    protected $session;

    /**
     * @param Session $session
     * @param EntityManager $entityManager
     * @param Logger $logger
     */
    public function __construct(Session $session, EntityManager $entityManager, Logger $logger)
    {
        $this->em      = $entityManager;
        $this->logger  = $logger;
        $this->session = $session;
    }

    /**
     * @param $url
     *
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @return array|\Doctrine\Common\Collections\ArrayCollection
     */
    public function parseFirst($url, Request $request)
    {
        $parsedUrl = parse_url($url);

        if (!$this->urlExist($parsedUrl['host'])) {
            $urlEntity   = new Entity\Url();
            $urlEntity->setUrl($parsedUrl['host']);

            if (array_key_exists('scheme', $parsedUrl)) {
                $urlEntity->setSchema($parsedUrl['scheme']);
            }

        } else {
            $urlEntity   = $this->em->getRepository('Ionafan2GetonlyimgBundle:Url')
                ->findOneBy(array('url'=>$parsedUrl['host']));
        }

        if (!$urlEntity->getXpathQuery()) {
            // @TODO Автоматическое определение XpathQuery
            $urlEntity->setXpathQuery("//div[@id='nfcontent']//div[@id='nfmaincont']//div[@class='content_full']//div[contains(@class , 'gallery')]/div[contains(@class , 'gallery_thumbnails')]/div[contains(@class , 'image-block')]/*/a");
        }

        if ($urlEntity->getImages()->count() > 0) {
            return $urlEntity->getImages();
        } else {

            $session = $request->getSession();

            if (!$session->get($url)) {

                $page  = $this->getPage($url);
                $html  = $page['content'];

                $session->set($url, $html);

            } else {
                 $html = $session->get($url);
            }

            //$tidyContent = $this->cleanRepair($html);
            $dom = new \DOMDocument();
            @$dom->loadHTML($html);
            $xpath = new \DOMXPath($dom);

            // xpath будет немнгого отлечаться от того что можно сгенерить в браузере
            // так как полученая страница отличаеться от той что в браузере
            $content = $xpath->query($urlEntity->getXpathQuery());



//                    $newdoc = new \DOMDocument;
//                    $node = $newdoc->importNode($a, true);
//                    $newdoc->appendChild($node);
//                    $html = $newdoc->saveHTML();
//
//                    $this->logger->debug($html);


            if ($content->length) {

                foreach($content as $aNode) {

                    /** @var $aNode \DOMElement */
                    $imgNode = $aNode->getElementsByTagName("img");

                    if ($imgNode) {

                        $imageEntity = new Entity\Image();

                        $href = $aNode->getAttribute('href');

                        $hrefParsed = parse_url($href);

                        if (array_key_exists('host', $hrefParsed)) {
                            $imageEntity->setLink($href);
                        } else {
                            $imageEntity->setLink($urlEntity->getSchema()."://".$urlEntity->getUrl().$href);
                        }

                        $urlEntity->setImages($imageEntity);
                    }
                }
            }
            $this->em->persist($urlEntity);
            $this->em->flush();

            return $urlEntity->getImages();
        }
    }

    /**
     *
     * @param $url
     *
     * @return array
     */
    public function parse($url)
    {
        $page  = $this->getPage($url);

        // Методы $this->getPage и $this->getDom отдельно так как между ними нужно распарсить url страницы.
        // А посколько искать могут по сокращенной ссылке то $url и $page['url'] могуть быть разными.
        $parsedUrl = parse_url($page['url']);

        $dom = $this->getDom($page);

        $data = array();

        // Получаем все изображения
        $imgs = $dom->getElementsByTagName('img');

        // Распределяем по группам
        // Имя группы это xpath без цифр
        $groups = array();
        foreach ($imgs as $candidat) {
            /** @var $candidat \DOMElement */
            $path = $candidat->getNodePath();
            // Удаляем все цифры из XPath
            $imgXpath = preg_replace('{\d}', '', $path);

             if ($candidat->parentNode->tagName == 'a') {
                 //@TODO: Что делать если ссылка на изображение видет не на изображение а на другую страницу пример vk.com?
                 $src =  $candidat->parentNode->getAttribute('href');
             } else {
                 $src = $candidat->getAttribute('src');
             }

            $fullImgSrc = $this->getFullImgSrc($parsedUrl, $src);

            // Отсекаем оденаковые изображения в каждой группе
            if (!in_array($fullImgSrc, $groups[$imgXpath])) {
                $groups[$imgXpath][] = $fullImgSrc;
            }
        }

        // Определяем самую численную группу
        // Потенциально опасное место (что если главных картинок меньше чем скажем иконок пользователей в комментах?
        $biggestGroup = current($groups);
        foreach ($groups as $group) {
            if (count($group) > count($biggestGroup)) {
                $biggestGroup = $group;
            }
        }

        return $biggestGroup;
    }

    /**
     * @param $parsedUrl
     * @param $src
     * @return string
     */
    private function getFullImgSrc($parsedUrl, $src)
    {
        $info = parse_url($src);
        if (!isset($info['host'])) {
            return $parsedUrl['scheme'] . "://" . $parsedUrl['host'] . $src;
        } else {
           return $src;
        }
    }

    /**
     * Получает информацию и странице по ссылке.
     * Корректно отработает даже если это не прямая ссылка на изображение.
     *
     * @param $url Ссылка на изображение
     * @TODO Как собрать контент всех страниц если есть пагинация?
     * @TODO Что делать если пагинация по скролу?
     * @TODO Заменить хранение в сессии на хранение в кеше
     * @return array Ассоциативный массив с ключами "content" и "url"
     */
    private function getPage($url)
    {
        if (!$this->session->get($url)) {

            $options = array(
                CURLOPT_RETURNTRANSFER => true, // return web page
                CURLOPT_HEADER         => false, // do not return headers
                CURLOPT_FOLLOWLOCATION => true, // follow redirects
                CURLOPT_USERAGENT      => "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.63 Safari/537.31", // who am i
                CURLOPT_AUTOREFERER    => true, // set referer on redirect
                CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
                CURLOPT_TIMEOUT        => 120, // timeout on response
                CURLOPT_MAXREDIRS      => 10, // stop after 10 redirects
            );

            $ch = curl_init($url);
            curl_setopt_array($ch, $options);
            $content = curl_exec($ch);
            $header  = curl_getinfo($ch);
            curl_close($ch);

            $res            = array();
            $res['content'] = $content;
            $res['url']     = $header['url'];
            $this->session->set($url, $res);

        } else {
             $res = $this->session->get($url);
        }
        return $res;

    }

    /**
     * @param $url
     *
     * @return bool
     */
    private function urlExist($url)
    {
        $repo = $this->em->getRepository('Ionafan2GetonlyimgBundle:Url');

        if ($repo->findOneBy(array('url'=>$url))) {
            return true;
        }

        return false;
    }

    /**
     * Получить DOMDocument страницы по указанному url
     *
     * @param $url
     *
     * @return \DOMDocument
     */
    private function getDom($page)
    {
        $dom = new \DOMDocument();
        // @TODO Избавиться от @ Понять насколько чревато ее использование тут и насколько это влияет на скорость приложения.
        @$dom->loadHTML($page['content']);

        return $dom;
    }

}
